#pragma once

#include <QObject>
#include <QMap>
#include <QList>
#include <QByteArray>
#include <QString>
#include <QThread>

#include <memory>

#include "State.h"
#include "Draft.h"
#include "ISimpleChannel.h"
#include "Tape.h"
#include "CharFactory.h"
#include "ActionFactory.h"


class Machine : public QThread
{
    Q_OBJECT

public:
    typedef QByteArray GlobalImpl;

    typedef QMap<QString, ISimpleChannelPtr> ChannelMap;
    typedef QMap<QString, GlobalImpl> GlobalMap;
    typedef QMap<QString, StatePtr> StateMap;
    typedef QMap<QString, TapePtr> TapeMap;

public:
    Machine(DraftPtr draft, const ChannelMap& channels);

public:
    GlobalMap&        Globals();
    const GlobalMap&  Globals() const;

    TapePtr           ControlTape();  
    TapePtr           ChannelTape(const QString& channel);
    ISimpleChannelPtr Channel(const QString& channel);
    StatePtr          CurrentState();

public:
    bool IsEnvString(const QString& key);
    QStringRef EnvStringRef(const QString& key);

public slots:
    void Stop(bool gentle);
    void Start();
    void PlaceTimeoutChar();
    void UpdateEnvString(const QString& key, const QString& value);

private slots:
    void ChangeState(StatePtr state);
    void ProcessData();

signals:
    void dataProcessed();
    void stopped();

private:
    void RegisterGlobals();
    void RegisterStates();
    void RegisterTransfers();
    void RegisterProcedures();
    
private:
    void SetStatesParent();
    void SetStatesAborts();
    void TuneUpTapes();
    void TuneUpChannels();

private:
    void CreateEnvStrings();

private:
    void PrepareState();
    void Finish();

private:
    CharTypePtr NextData();
    CharTypePtr NextCtrl();

private:
    StatePtr GetRegisteredState(const QString& state);
    TransferPtr GetBlankTransfer();
    void RegisterTerminalTransfer(StatePtr state, CharTypePtr character, StatePtr terminal);

private:
    bool IsTerminalState(StatePtr state);

private:
    GlobalMap         _globals;
    StateMap          _registeredStates;
    StatePtr          _currentState;

private:
    DraftPtr          _draft;

private:
    TapePtr           _ctrlTape; 
    ChannelMap        _channels;
    TapeMap           _tapes;

private: //exclude these from Machine and make them singletones
    ActionFactoryPtr  _actionFactory;
    CharFactoryPtr    _charFactory;

private:
    StringMap         _envStrings;
    QMutex            _envChangeLock;
};

typedef std::tr1::shared_ptr<Machine> MachinePtr; 